#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>

/*
 * 可靠信号与不可靠信号
 */

#if 0

不可靠信号： 也称为非实时信号，不支持排队，信号可能会丢失, 比如发送多次相同的信号, 进程只能收到一次，信号值取值区间为1~31。
可靠信号： 也称为实时信号，支持排队, 信号不会丢失, 发多少次, 就可以收到多少次. 信号值取值区间为32~64。

/* 实时信号与不可靠信号的区别 */
主要就是实时信号支持排队不会丢失。（实时信号还有一个特点，即到达的顺序是可以保证的）


通过系统调用，发送signal信号：kill()，raise()，sigqueue()，alarm()，setitimer()，abort()


/* 在队列中向指定进程发送一个信号和数据 */
/* 新的发送信号系统调用,主要是针对实时信号提出的支持信号带有参数,与函数sigaction()配合使用 */
int sigqueue(pid_t pid, int sig, const union sigval value);

    sigqueue的第一个参数是指定接收信号的进程id，
    第二个参数确定即将发送的信号，
    第三个参数是一个联合数据结构union sigval，指定了信号传递的参数，即通常所说的4字节值。

    成功返回0,失败返回-1 

typedef union sigval
{ 
    int sival_int; 
    void *sival_ptr; 
}sigval_t; 
 

sigqueue()比kill()传递了更多的附加信息，但sigqueue()只能向一个进程发送信号，而不能发送信号给一个进程组。
 


#endif

void catch_signal(int signo, siginfo_t *info, void *p)
{
    switch (signo)
    {
        case SIGINT:
            printf("accept SIGINT!  recv data=%d\n",info->si_value.sival_int);
            break;
        case 34:
            //SIGRTMIN似乎不是一个确定的int类型
            printf("accept SIGRTMIN! recv data=%d\n",info->si_value.sival_int);
            break;
        case SIGUSR1:
            printf("accept SIGUSR1!\n");
            //取消信号阻塞
            sigset_t uset;
            sigemptyset(&uset);
            sigaddset(&uset, SIGINT);
            sigaddset(&uset, SIGRTMIN);
            sigprocmask(SIG_UNBLOCK, &uset, NULL);
            printf("阻塞解除了！\n");
            break;
    }
}

int main(int arg, char *args[])
{
    pid_t pid = 0;
    struct sigaction act;
    act.sa_sigaction = catch_signal;
    sigemptyset(&act.sa_mask);
    act.sa_flags = SA_SIGINFO;
    //注册SIGINT信号
    if (sigaction(SIGINT, &act, NULL) != 0)
    {
        printf("sigaction SIGINT failed !\n");
        return -1;
    }
    //注册SIGTMIN信号
    if (sigaction(SIGRTMIN, &act, NULL) != 0)
    {
        printf("sigaction SIGINT failed !\n");
        return -1;
    }
    //注册SIGUSR1信号
    if (sigaction(SIGUSR1, &act, NULL) != 0)
    {
        printf("sigaction SIGINT failed !\n");
        return -1;
    }
    //阻塞SIGINT信号和SIGTMIN信号
    sigset_t bset;
    sigemptyset(&bset);
    sigaddset(&bset, SIGINT);
    sigaddset(&bset, SIGRTMIN);
    //更新进程屏蔽信号状态字
    if (sigprocmask(SIG_BLOCK, &bset, NULL) != 0)
    {
        printf("sigprocmask() failed !\n");
        return -1;
    }
    pid = fork();
    if (pid == -1)
    {
        printf("fork() failed ! error message:%s\n", strerror(errno));
        return -1;
    }
    if (pid == 0)
    {
        int i = 0, ret = 0;
        union sigval v1;
        union sigval v2;
        for (i = 0; i < 3; i++)
        {
            v1.sival_int = 201 + i;
            ret = sigqueue(getppid(), SIGINT, v1);
            if (ret != 0)
            {
                printf("发送不可靠信号SIGINT失败! error message:%s\n", strerror(errno));
            } else
            {
                printf("发送不可靠信号SIGINT成功！\n");
            }
        }
        for (i = 0; i < 3; i++)
        {
            v2.sival_int = 301 + i;
            ret = sigqueue(getppid(), SIGRTMIN, v2);
            if (ret != 0)
            {
                printf("发送可靠信号SIGTMIN失败! error message:%s\n", strerror(errno));
            } else
            {
                printf("发送可靠信号SIGTMIN成功！\n");
            }
        }
        //发送SIGUSR1信号
        if (kill(getppid(), SIGUSR1) != 0)
        {
            printf("kill() failed ! error message；%s\n", strerror(errno));
        }
        exit(0);
    }
    //父进程
    int res = 0, status = 0;
    while (1)
    {
        res = wait(&status);
        if (res == -1)
        {
            if (errno == EINTR)
            {
                continue;
            }
            break;
        }
    }
    while (1)
    {
        sleep(1);
    }
    return 0;
}

#if 0

一：SIGINT是不可靠信号。发送了3次父进程只接收到1次，SIGRTMIN是可靠信号，发送了3次父进程接收到3次信号。
二：对于可靠信号，Linux内核会缓存可靠信号，Linux内核可以缓存8192（各个Linux版本不同）条可靠信号；
    对于不可靠信号，Linux只能缓存一条不可靠信号。
三：执行命令行： ulimit -a
    查看Linux支持的信号性能参数
四：发送信号的数量超过系统上限，将会发送失败

#endif
